package guru.mercurio.eps.sSMPP;

import guru.mercurio.common.PhoneAddress;
import guru.mercurio.common.RelativeApplicationAddress;
import guru.merlin.core.AbstractEnvelopeProcessor;
import guru.merlin.core.ConfigurationCantHandException;
import guru.merlin.core.CantHandException;
import guru.merlin.core.EnvelopeCantHandException;
import guru.merlin.core.PeerCantHandException;
import guru.merlin.core.Envelope;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.smpp.pdu.Address;
import org.smpp.pdu.DeliverSM;

/**
 * @author Santiago Ventura
 *
 */
public class SmppEP extends AbstractEnvelopeProcessor implements guru.mercurio.eps.sSMPP.DataHandler{
    static Logger logger = Logger.getLogger(SmppEP.class);
    private Properties props = new Properties();
    private String operatorShortName;
    private SMPPOperator smppOperator = null;
    private String paramFilePath;
    ConnectionUp up = null;
    
    /**
     * Constructor sin argumentos.
     *
     * @see java.lang.Object#Object()
     */
    public SmppEP() {
    }
    
    /**
     * @see guru.merlin.core.EnvelopeProcessor#openConnection(java.lang.String)
     */
    public void openConnection(String parametersFilePath) throws CantHandException{//MOD SANTIAGO
        //this.smppOperator=null;
        this.paramFilePath=parametersFilePath;
        InputStream fis;
        try {
            fis = new FileInputStream(parametersFilePath);
            props.load(fis);
            fis.close();
            this.smppOperator = new SMPPOperator("default", parametersFilePath, "", this);
        } catch (IOException e1) {
            logger.error("", e1);
            throw new ConfigurationCantHandException("Properties file can't be loaded: " + e1.toString());
        }
        
        if ((operatorShortName = props.getProperty("operatorShortName")) == null)
            throw new ConfigurationCantHandException("no 'operator Short Name' in properties file.");
        
        
        smppOperator.openConnection();
        smppOperator.getBind();
    }
    
    /**
     * @see guru.merlin.core.EnvelopeProcessor#closeConnection()
     */
    public void closeConnection() throws CantHandException {//MOD SANTIAGO
        if (smppOperator.isBound()) {
            if (smppOperator.getToReceiver().isAlive()) {
                smppOperator.stopReceiveSMS();
            }
            smppOperator.closeBind();
            smppOperator.closeConnection();
        }
    }
    
    /**
     * @throws EPInfrastructureException
     * @see guru.merlin.core.EnvelopeProcessor#start()
     */
    public void start() throws CantHandException{//MOD SANTIAGO
        //Direction direcction = Direction.transceiver; //OJO: que es esto?
        //int r = smppOperator.getBind();
        try {
            if (smppOperator==null || smppOperator.getSession()==null || this.isConnected()==false){  //MOD SANTIAGO     
                this.openConnection(this.paramFilePath);
            }
            if (this.isConnected()==true) {      
                smppOperator.receiveSMS();
                this.keepConnectionUp();            
            }
        } catch (Exception e) {
            throw new PeerCantHandException(e.toString());
        }
    }
    
    /**
     * @see guru.merlin.core.EnvelopeProcessor#stop()
     */
    public void stop() {
        try {
            smppOperator.stopReceiveSMS();
            //closeConnection(); --->Comentado 23/08/2006 para modificar el stop del nodo
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public boolean isConnected () {//MOD SANTIAGO        
        return smppOperator.isConnected ();
    }    
    
    /**
     * @see guru.merlin.core.EnvelopeProcessor#areYouAlive()
     */
    public boolean areYouAlive() {
        return true;
    }
    
    /**
     * @see guru.merlin.core.EnvelopeHandler#handEnvelope(guru.merlin.common.Envelope)
     */
    public Object handEnvelope(Envelope envelope) throws CantHandException {
        String msg = "";
        if (envelope==null) {
            throw new EnvelopeCantHandException("Envelope Cant be null.");
        }
        PhoneAddress destinationPhone;
        RelativeApplicationAddress originAddress;
        
        logger.info("Envelope received for dispatching:" + envelope.toStringNoContent());
        if (!(envelope.getDestination() instanceof PhoneAddress)) {
            throw new EnvelopeCantHandException("Destination is not a Phone Number");
        }
        destinationPhone = (PhoneAddress) envelope.getDestination();
        
        if (!(envelope.getOrigin() instanceof RelativeApplicationAddress)) {
            throw new EnvelopeCantHandException("Origin is not a RelativeApplicationAddress");
        }
        originAddress = (RelativeApplicationAddress) envelope.getOrigin();
        
        if ((envelope.getEnvelopeReplyTo() != null) && (destinationPhone.getareacode() == 414)) {
            long elapsedTime = new Date().getTime() - envelope.getEnvelopeReplyTo().getDate().getTime();
            logger.debug("The message has a time of life from the origin of " + elapsedTime + " milliseconds.");
            long waitTime = 20 * 1000 - elapsedTime;
            
            if (waitTime > 0) {
                logger.debug("It will be expected " + waitTime + " milliseconds.");
                
                try {
                    // Thread.currentThread().wait(waitTime);
                    doSleep(waitTime);
                    logger.debug("It was already expected" + waitTime + " milliseconds.");
                } catch (Exception e) {
                    msg = "it was not possible to be waited for " + waitTime + " milliseconds.  There was an exception that woke up the Thread before time ";
                    logger.error(msg, e);
                }
            }
        }
        
        smppOperator.submitSMS(destinationPhone, envelope.getContent().toString(), originAddress.getPort(), Integer.parseInt(envelope.getId()));
        
        return envelope;
    }
    
    /**
     * Method keepConnection.
     */
    public void keepConnectionUp() { //MOD SANTIAGO
        if (up==null){
            up = new ConnectionUp(this);
            up.initThread();
        }
    }
    
    public synchronized void doSleep(long waitTime) throws InterruptedException {
        Thread.sleep(waitTime);
    }
    
    /**
     * @see guru.mercurio.eps.sSMPP.DataHandler#put(String, ReceivedMessage) Ejemplo del Payload de
     *      acuce de recibo: "id:1198575019 sub:001 dlvrd:001 submit date:0505061603 done
     *      date:0505061604 stat:DELIVRD err:000 text:si recibi el mensaje"
     */
    public void put(DeliverSM receivedMessage) {
        
        String msg = "";
        try {
            //PhoneAddress destAddress = null;
            PhoneAddress srcAddress = null;
            RelativeApplicationAddress relativeApplicationAddress;
            Envelope env;
            String altMsg;
            
            Address destAdd = receivedMessage.getDestAddr();
            Address srcAdd = receivedMessage.getSourceAddr();            
            msg = "Received Message Source Address: NPI["+srcAdd.getNpi()+"] TON["+srcAdd.getTon()+"] SRC["+srcAdd.getAddress()+"]"+"] TO["+destAdd.getAddress()+"]";
            switch (srcAdd.getTon()) {
                case 1:
                    String prefix = srcAdd.getAddress().substring(0, 3);
                    String nlocal = srcAdd.getAddress().substring((srcAdd.getAddress().length()-7),srcAdd.getAddress().length());
                    
                    if (prefix.equals("034")){//Digitel (03458412xxxxxxx)
                        srcAddress = new PhoneAddress("58", "412", nlocal);
                    } else if (prefix.equals("027")){//Movistar (0270414xxxxxxx)
                        srcAddress = new PhoneAddress("58", "414", nlocal);
                    }else if (prefix.equals("004")){//Movilnet (004xxxxxxx)
                        srcAddress = new PhoneAddress("58", "416", nlocal);
                    } else if (prefix.equals("049")) {//Infonet (04958418xxxxxxx)
                        srcAddress = new PhoneAddress("58", "418", nlocal);
                    } else if (srcAdd.getAddress().substring(0, 2).equals("58")&&(srcAdd.getAddress().length()==12)){
                        srcAddress = new PhoneAddress(srcAdd.getAddress().substring(0, 2), srcAdd.getAddress().substring(2, 5), srcAdd.getAddress().substring(5));
                    } else if (prefix.equals("078")) {//Panama Movistar
                        nlocal = srcAdd.getAddress().substring((srcAdd.getAddress().length()-8),srcAdd.getAddress().length());
                        srcAddress = new PhoneAddress("507", "0", nlocal);
                    } else if (prefix.equals("152")) {//Panama GSM
                        nlocal = srcAdd.getAddress().substring((srcAdd.getAddress().length()-8),srcAdd.getAddress().length());
                        srcAddress = new PhoneAddress("507", "0", nlocal);
                    } else if (prefix.equals("117")) {//Panama C&W
                        nlocal = srcAdd.getAddress().substring((srcAdd.getAddress().length()-8),srcAdd.getAddress().length());
                        srcAddress = new PhoneAddress("507", "0", nlocal);
                    } else if (srcAdd.getAddress().substring(0, 2).equals("58")&&(srcAdd.getAddress().length()==12)){
                        srcAddress = new PhoneAddress("58", srcAdd.getAddress().substring(2, 5), srcAdd.getAddress().substring(5));
                    } else if (srcAdd.getAddress().length()<12){
                        srcAddress = new PhoneAddress("58", srcAdd.getAddress().substring(1, 4), srcAdd.getAddress().substring(4));
                    }else {
                        logger.info("Is not a destination valid "+msg);
                    }            
                    break;
                case 2:
                    if (srcAdd.getAddress().substring(1, 3).equals("58")) {
                        srcAddress = new PhoneAddress("58", "416", srcAdd.getAddress().substring(3));
                    } else if (srcAdd.getAddress().substring(0, 2).equals("58")&&(srcAdd.getAddress().length()==12)){
                        srcAddress = new PhoneAddress(srcAdd.getAddress().substring(0, 2), srcAdd.getAddress().substring(2, 5), srcAdd.getAddress().substring(5));
                    } else if (srcAdd.getAddress().length()<12){
                        srcAddress = new PhoneAddress("58", srcAdd.getAddress().substring(1, 4), srcAdd.getAddress().substring(4));
                    } else {
                        logger.info("Is not a destination valid "+msg);
                    }
                    break;
                default:
                    throw new ConfigurationCantHandException("Desconozco el TON utilizado para codificar el srcAddress");
                    
            }            
            logger.info(msg);            
            
            if (srcAddress!=null){
                logger.debug("Phone Address: "+srcAddress);
            }
            
            if (destAdd.getAddress().length() < 10) {
                if (destAdd.getAddress().substring(0, 3).trim().equalsIgnoreCase("139")) {
                    relativeApplicationAddress = new RelativeApplicationAddress(operatorShortName, destAdd.getAddress().substring(3));
                } else {
                    relativeApplicationAddress = new RelativeApplicationAddress(operatorShortName, destAdd.getAddress());
                }
                
                logger.debug("ApplicationAddress=" + relativeApplicationAddress);
                
                if ((receivedMessage.getSmLength() == 0) && (receivedMessage.hasMessagePayload())) {
                    logger.debug("hasPayloadType=" + receivedMessage.hasPayloadType());
                    if (receivedMessage.hasPayloadType()) {
                        logger.debug("getPayloadType=" + receivedMessage.getPayloadType());
                    }
                    
                    if ((altMsg = new String(receivedMessage.getMessagePayload().getBuffer())).startsWith("id:")) {
                        logger.debug("MessagePayload=" + altMsg+". Rejected");
                        return;
                    }
                    
                    env = new Envelope(altMsg, relativeApplicationAddress, srcAddress);
                    
                } else {
                    String content = "";
                    if (receivedMessage.getShortMessage() != null && !receivedMessage.getShortMessage().equals("")) {
                        content = receivedMessage.getShortMessage().trim();
                        if (receivedMessage.getShortMessage().startsWith("(" + srcAddress.getLocal() + ")")) {
                            content = receivedMessage.getShortMessage().replaceFirst("(" + srcAddress.getLocal() + ")", "").trim();
                            if (content.startsWith("()")) {
                                content = content.substring(2).trim();
                            }
                        }
                    }
                    
                    env = new Envelope(content, relativeApplicationAddress, srcAddress);
                }
                
            } else {
                logger.warn("The received message is directed to a telephone number and not to a SC.  The message was rejected.");
                return; //este mensaje no debería ser desechado aqui... debería desecharlo en enrutador.
            }
            logger.info("Envelope=" + env.toStringNoContent());
            getEnvelopeHandler().handEnvelope(env);
            logger.info ("retornando del Container");
            
        } catch (Exception e) {
            e.printStackTrace ();
            logger.error(e);
        }
    }
    
    /* (sin Javadoc)
     * @see guru.merlin.core.EnvelopeHandler#retrieveEnvelope(java.lang.Object)
     */
    public void retrieveEnvelope(Object EnvelopeReference) throws CantHandException {
        
    }
    
    /* (sin Javadoc)
     * @see guru.merlin.core.EnvelopeHandler#commitHandEnvelope(java.lang.Object)
     */
    public void commitHandEnvelope(Object EnvelopeReference) throws CantHandException {
        
    }
    
    
    /**
     * @return Devuelve smppOperator.
     */
    public SMPPOperator getSmppOperator() {
        return smppOperator;
    }
    /**
     * @param smppOperator El smppOperator a establecer.
     */
    public void setSmppOperator(SMPPOperator smppOperator) {
        this.smppOperator = smppOperator;
    }
    /**
     * @return Devuelve operatorShortName.
     */
    public String getOperatorShortName() {
        return operatorShortName;
    }
    /**
     * @param operatorShortName El operatorShortName a establecer.
     */
    public void setOperatorShortName(String operatorShortName) {
        this.operatorShortName = operatorShortName;
    }
    /**
     * @return Devuelve props.
     */
    public Properties getProps() {
        return props;
    }
    /**
     * @param props El props a establecer.
     */
    public void setProps(Properties props) {
        this.props = props;
    }
}